<?php
namespace app\common\library;

class WxPayV3
{
    private $mchid;          // 商户号
    private $appid;          // 小程序/公众号APPID
    private $v3Key;          // APIv3密钥
    private $serialNo;       // 商户证书序列号
    private $privateKey;     // 商户私钥
    private $platformCert;   // 微信支付平台证书

    public function __construct($config)
    {
        $this->mchid = $config['mchid'];
        $this->appid = $config['appid'];
        $this->v3Key = $config['v3_key'];
        $this->serialNo = $config['serial_no'];

        // 加载商户私钥
        $this->privateKey = openssl_get_privatekey(
            file_get_contents($config['key_path'])
        );

        // 加载平台证书
        $this->platformCert = openssl_x509_read(
            file_get_contents($config['platform_cert_path'])
        );
    }

    /**
     * JSAPI下单
     */
    public function jsapiOrder($params)
    {
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';

        $data = [
            'appid'        => $this->appid,
            'mchid'        => $this->mchid,
            'description' => $params['description'],
            'out_trade_no' => $params['out_trade_no'],
            'notify_url'   => $params['notify_url'],
            'amount'       => [
                'total'    => intval($params['total']), // 单位:分
                'currency' => 'CNY'
            ],
            'payer'        => [
                'openid'   => $params['openid']
            ]
        ];

        return $this->request('POST', $url, $data);
    }

    /**
     * 小程序下单
     */
    public function miniProgramOrder($params)
    {
        $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';

        $data = [
            'appid'        => $this->appid,
            'mchid'        => $this->mchid,
            'description'  => $params['description'],
            'out_trade_no' => $params['out_trade_no'],
            'notify_url'   => $params['notify_url'],
            'amount'       => [
                'total'    => intval($params['total']), // 单位:分
                'currency' => 'CNY'
            ],
            'payer'        => [
                'openid'   => $params['openid']
            ]
        ];

        return $this->request('POST', $url, $data);
    }

    /**
     * 生成签名
     */
    private function sign($message)
    {
        openssl_sign($message, $signature, $this->privateKey, 'sha256WithRSAEncryption');
        return base64_encode($signature);
    }

    /**
     * 构造Authorization
     */
    private function buildAuth($method, $url, $body = '')
    {
        $timestamp = time();
        $nonce = uniqid();
        $urlParts = parse_url($url);
        $canonicalUrl = ($urlParts['path'] ?? '/') . (isset($urlParts['query']) ? '?' . $urlParts['query'] : '');

        $message = $method . "\n" .
            $canonicalUrl . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";

        $signature = $this->sign($message);

        $schema = 'WECHATPAY2-SHA256-RSA2048';
        $token = sprintf(
            'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $this->mchid,
            $nonce,
            $timestamp,
            $this->serialNo,
            $signature
        );

        return $schema . ' ' . $token;
    }

    /**
     * 发送请求
     */
    private function request($method, $url, $data = [])
    {
        $body = $data ? json_encode($data, JSON_UNESCAPED_UNICODE) : '';
        $auth = $this->buildAuth($method, $url, $body);

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
        }

        $headers = [
            'Accept: application/json',
            'Content-Type: application/json',
            'User-Agent: FastAdmin/WxPayV3',
            'Authorization: ' . $auth
        ];

        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

        $response = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($error) {
            throw new \Exception("CURL Error: " . $error);
        }

        return json_decode($response, true);
    }

    /**
     * 解密回调通知
     */
    public function decryptNotify($headers, $body)
    {
        $nonce = $headers['Wechatpay-Nonce'];
        $signature = $headers['Wechatpay-Signature'];
        $timestamp = $headers['Wechatpay-Timestamp'];
        $serial = $headers['Wechatpay-Serial'];

        // 验证签名
        $message = $timestamp . "\n" . $nonce . "\n" . $body . "\n";
        $signature = base64_decode($signature);
        $result = openssl_verify($message, $signature, $this->platformCert, 'sha256WithRSAEncryption');

        if ($result !== 1) {
            throw new \Exception('Invalid signature');
        }

        $data = json_decode($body, true);
        $ciphertext = base64_decode($data['resource']['ciphertext']);
        $associatedData = $data['resource']['associated_data'];
        $nonce = $data['resource']['nonce'];

        // 解密数据
        $decrypted = openssl_decrypt(
            $ciphertext,
            'aes-256-gcm',
            $this->v3Key,
            OPENSSL_RAW_DATA,
            $nonce,
            $associatedData
        );

        return json_decode($decrypted, true);
    }
}